﻿/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * Author: Laurent WOUTERS
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Hime.Redist.Parsers;

namespace xowl.gmi.compiler.model
{
    class Model
    {
        private int nextID;
        private List<string> classes;
        private List<string> objProperties;
        private List<string> dataProperties;
        private Dictionary<string, List<string>> domains;
        private Dictionary<string, string> dataRanges;
        private Dictionary<string, List<string>> objRanges;
        private Dictionary<string, bool> isFunctional;
        private Dictionary<string, bool> isOrdered;

        public ICollection<string> Classes { get { return classes; } }
        public ICollection<string> ObjectProperties { get { return objProperties; } }
        public ICollection<string> DataProperties { get { return dataProperties; } }
        public bool IsOrdered(string property) { return isOrdered[property]; }

        public Model()
        {
            nextID = 0;
            classes = new List<string>();
            objProperties = new List<string>();
            dataProperties = new List<string>();
            domains = new Dictionary<string, List<string>>();
            dataRanges = new Dictionary<string, string>();
            objRanges = new Dictionary<string, List<string>>();
            isFunctional = new Dictionary<string, bool>();
            isOrdered = new Dictionary<string, bool>();
        }

        public string GetPlural(string name)
        {
            if (name[name.Length - 1] == 'y')
                return name.Substring(0, name.Length - 1) + "ies";
            else
                return name + "s";
        }

        public string GetCommonPrefix(List<string> names)
        {
            names.Sort(new Comparison<string>(CompareStringLength));
            string prefix = string.Empty;
            for (int i = 0; i != names[0].Length - 1; i++)
            {
                char c = names[0][i];
                for (int j = 1; j != names.Count; j++)
                    if (names[j][i] != c)
                        return prefix;
                prefix += c;
            }
            return prefix;
        }
        private int CompareStringLength(string s1, string s2) { return s1.Length - s2.Length; }

        private int GetNextID()
        {
            int result = nextID;
            nextID++;
            return result;
        }

        public void AddClass(string c)
        {
            if (!classes.Contains(c))
                classes.Add(c);
        }

        private void CreateObjProperty(string property, bool functional, bool ordered)
        {
            objProperties.Add(property);
            domains.Add(property, new List<string>());
            objRanges.Add(property, new List<string>());
            isFunctional[property] = functional;
            isOrdered[property] = ordered;
        }
        private void CreateDataProperty(string property, string range)
        {
            dataProperties.Add(property);
            domains.Add(property, new List<string>());
            dataRanges[property] = range;
            isFunctional[property] = true;
            isOrdered[property] = false;
        }

        public string AddObjProperty(string property, string domain, string range, bool functional, bool ordered)
        {
            if (!objProperties.Contains(property))
                CreateObjProperty(property, functional, ordered);
            else if (isFunctional[property] != functional || isOrdered[property] != ordered)
            {
                property = property + GetNextID();
                CreateObjProperty(property, functional, ordered);
            }
            if (!domains[property].Contains(property))
                domains[property].Add(domain);
            if (!objRanges[property].Contains(range))
                objRanges[property].Add(range);
            isFunctional[property] = functional;
            return property;
        }

        public string AddDataProperty(string property, string domain, string range)
        {
            if (!dataProperties.Contains(property))
                CreateDataProperty(property, range);
            else if (dataRanges[property] != range)
            {
                property = property + GetNextID();
                CreateDataProperty(property, range);
            }
            if (!domains[property].Contains(domain))
                domains[property].Add(domain);
            return property;
        }

        public void WriteOWL(StreamWriter writer)
        {
            foreach (string c in classes)
                WriteClass(writer, c);
            foreach (string prop in objProperties)
            {
                writer.WriteLine();
                WriteObjectProperty(writer, prop);
            }
            foreach (string prop in dataProperties)
            {
                writer.WriteLine();
                WriteDataProperty(writer, prop);
            }
        }

        private void WriteClass(StreamWriter writer, string c)
        {
            writer.WriteLine("\tDeclaration(Class(:" + c + "))");
        }
        private void WriteObjectProperty(StreamWriter writer, string property)
        {
            if (isFunctional[property])
                writer.WriteLine("\tFunctionalObjectProperty(:" + property + ")");
            if (domains[property].Count == 1)
                writer.WriteLine("\tObjectPropertyDomain(:" + property + " :" + domains[property][0] + ")");
            else
            {
                writer.Write("\tObjectPropertyDomain(:" + property + " ObjectUnionOf(");
                for (int i = 0; i != domains[property].Count; i++)
                {
                    if (i != 0)
                        writer.Write(" ");
                    writer.Write(":" + domains[property][i]);
                }
                writer.WriteLine("))");
            }
            if (objRanges[property].Count == 1)
                writer.WriteLine("\tObjectPropertyRange(:" + property + " :" + objRanges[property][0] + ")");
            else
            {
                writer.Write("\tObjectPropertyRange(:" + property + " ObjectUnionOf(");
                for (int i = 0; i != objRanges[property].Count; i++)
                {
                    if (i != 0)
                        writer.Write(" ");
                    writer.Write(":" + objRanges[property][i]);
                }
                writer.WriteLine("))");
            }
        }
        private void WriteDataProperty(StreamWriter writer, string property)
        {
            if (isFunctional[property])
                writer.WriteLine("\tFunctionalDataProperty(:" + property + ")");
            if (domains[property].Count == 1)
                writer.WriteLine("\tDataPropertyDomain(:" + property + " :" + domains[property][0] + ")");
            else
            {
                writer.Write("\tDataPropertyDomain(:" + property + " ObjectUnionOf(");
                for (int i = 0; i != domains[property].Count; i++)
                {
                    if (i != 0)
                        writer.Write(" ");
                    writer.Write(":" + domains[property][i]);
                }
                writer.WriteLine("))");
            }
            writer.WriteLine("\tDataPropertyRange(:" + property + " xsd:" + dataRanges[property] + ")");
        }
    }
}
